/*
 *------------------------------------------------------------------------------
 * @File      :    tp9950_dvp.c
 * @Date      :    2021-7-29
 * @Author    :    lomboswer <lomboswer@lombotech.com>
 * @Brief     :    Source file for MDP(Media Development Platform).
 *
 * Copyright (C) 2020-2021, LomboTech Co.Ltd. All rights reserved.
 *------------------------------------------------------------------------------
 */
#include "ei_comm_camera.h"
#include "camera_usr.h"
#include <pthread.h>
#include <stdlib.h>

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */

#define SNS_ID 9950
#define SNS_NAME  "tp9950_dvp"
#define DEV_TYPE DVP

static SNS_STATE_S g_sastSns[VISS_MAX_DEV_NUM];
/*8 bit bus-width bt656*/

typedef struct eiCAMERA_S {
    pthread_mutex_t mutex;
} CAMERA_S;

static const SNS_REG_S s_aBt656_1080p_1ch_30fps[] = {
	{ 0x02, 0xCC },
	{ 0x05, 0x00 },
	{ 0x06, 0x32 },
	{ 0x07, 0xC0 },
	{ 0x08, 0x00 },
	{ 0x09, 0x24 },
	{ 0x0A, 0x48 },
	{ 0x0B, 0xC0 },
	{ 0x0C, 0x03 },
	{ 0x0D, 0x72 },
	{ 0x0E, 0x00 },
	{ 0x0F, 0x00 },
	{ 0x10, 0x00 },
	{ 0x11, 0x40 },
	{ 0x12, 0x60 },
	{ 0x13, 0x00 },
	{ 0x14, 0x00 },
	{ 0x15, 0x01 },
	{ 0x16, 0xF0 },
	{ 0x17, 0x80 },
	{ 0x18, 0x29 },
	{ 0x19, 0x38 },
	{ 0x1A, 0x47 },
	{ 0x1B, 0x01 },
	{ 0x1C, 0x08 },
	{ 0x1D, 0x98 },
	{ 0x1E, 0x80 },
	{ 0x1F, 0x80 },
	{ 0x20, 0x38 },
	{ 0x21, 0x46 },
	{ 0x22, 0x36 },
	{ 0x23, 0x3C },
	{ 0x24, 0x04 },
	{ 0x25, 0xFE },
	{ 0x26, 0x0D },
	{ 0x27, 0x2D },
	{ 0x28, 0x00 },
	{ 0x29, 0x48 },
	{ 0x2A, 0x30 },
	{ 0x2B, 0x60 },
	{ 0x2C, 0x3A },
	{ 0x2D, 0x54 },
	{ 0x2E, 0x40 },
	{ 0x2F, 0x00 },
	{ 0x30, 0xA5 },
	{ 0x31, 0x95 },
	{ 0x32, 0xE0 },
	{ 0x33, 0x60 },
	{ 0x34, 0x00 },
	{ 0x35, 0x05 },
	{ 0x36, 0xDC },
	{ 0x37, 0x00 },
	{ 0x38, 0x00 },
	{ 0x39, 0x1C },
	{ 0x3A, 0x32 },
	{ 0x3B, 0x26 },
	{ 0x3C, 0x00 },
	{ 0x3D, 0x60 },
	{ 0x3E, 0x00 },
	{ 0x3F, 0x00 },
	{ 0x40, 0x00 },
	{ 0x41, 0x00 },
	{ 0x42, 0x00 },
	{ 0x43, 0x00 },
	{ 0x44, 0x00 },
	{ 0x45, 0x00 },
	{ 0x46, 0x00 },
	{ 0x47, 0x00 },
	{ 0x48, 0x00 },
	{ 0x49, 0x00 },
	{ 0x4A, 0x00 },
	{ 0x4B, 0x00 },
	{ 0x4C, 0x43 },
	{ 0x4D, 0x00 },
	{ 0x4E, 0x17 },
	{ 0x4F, 0x00 },

	{ 0xF0, 0x00 },
	{ 0xF1, 0x00 },
	{ 0xF2, 0x00 },
	{ 0xF3, 0x07 },
	{ 0xF4, 0x20 },
	{ 0xF5, 0x10 },
	{ 0xF6, 0x00 },
	{ 0xF7, 0x00 },
	{ 0xF8, 0x00 },
	{ 0xF9, 0x00 },
	{ 0xFA, 0x03 },
	{ 0xFB, 0x00 },
	{ 0xFC, 0x00 },

	{ 0x40, 0x08 },
	{ 0x00, 0x00 },
	{ 0x01, 0xf8 },
	{ 0x02, 0x00 },
	{ 0x08, 0xF0 },
	{ 0x13, 0x04 },
	{ 0x14, 0x73 },
	{ 0x15, 0x08 },
	{ 0x20, 0x12 },
	{ 0x34, 0x1b },
	{ 0x23, 0x02 },
	{ 0x23, 0x00 },

	{ 0x40, 0x00 },

};

static const SNS_REG_S s_aBt656_1080p_1ch_25fps[] = {
	{ 0x02, 0xCC },
	{ 0x05, 0x00 },
	{ 0x06, 0x32 },
	{ 0x07, 0xC0 },
	{ 0x08, 0x00 },
	{ 0x09, 0x24 },
	{ 0x0A, 0x48 },
	{ 0x0B, 0xC0 },
	{ 0x0C, 0x03 },
	{ 0x0D, 0x73 },
	{ 0x0E, 0x00 },
	{ 0x0F, 0x00 },
	{ 0x10, 0x00 },
	{ 0x11, 0x40 },
	{ 0x12, 0x60 },
	{ 0x13, 0x00 },
	{ 0x14, 0x00 },
	{ 0x15, 0x01 },
	{ 0x16, 0xF0 },
	{ 0x17, 0x80 },
	{ 0x18, 0x29 },
	{ 0x19, 0x38 },
	{ 0x1A, 0x47 },
	{ 0x1B, 0x01 },
	{ 0x1C, 0x0A },
	{ 0x1D, 0x50 },
	{ 0x1E, 0x80 },
	{ 0x1F, 0x80 },
	{ 0x20, 0x3C },
	{ 0x21, 0x46 },
	{ 0x22, 0x36 },
	{ 0x23, 0x3C },
	{ 0x24, 0x04 },
	{ 0x25, 0xFE },
	{ 0x26, 0x0D },
	{ 0x27, 0x2D },
	{ 0x28, 0x00 },
	{ 0x29, 0x48 },
	{ 0x2A, 0x30 },
	{ 0x2B, 0x60 },
	{ 0x2C, 0x3A },
	{ 0x2D, 0x54 },
	{ 0x2E, 0x40 },
	{ 0x2F, 0x00 },
	{ 0x30, 0xA5 },
	{ 0x31, 0x86 },
	{ 0x32, 0xFB },
	{ 0x33, 0x60 },
	{ 0x34, 0x00 },
	{ 0x35, 0x05 },
	{ 0x36, 0xDC },
	{ 0x37, 0x00 },
	{ 0x38, 0x00 },
	{ 0x39, 0x1C },
	{ 0x3A, 0x32 },
	{ 0x3B, 0x26 },
	{ 0x3C, 0x00 },
	{ 0x3D, 0x60 },
	{ 0x3E, 0x00 },
	{ 0x3F, 0x00 },
	{ 0x40, 0x00 },
	{ 0x41, 0x00 },
	{ 0x42, 0x00 },
	{ 0x43, 0x00 },
	{ 0x44, 0x00 },
	{ 0x45, 0x00 },
	{ 0x46, 0x00 },
	{ 0x47, 0x00 },
	{ 0x48, 0x00 },
	{ 0x49, 0x00 },
	{ 0x4A, 0x00 },
	{ 0x4B, 0x00 },
	{ 0x4C, 0x43 },
	{ 0x4D, 0x00 },
	{ 0x4E, 0x17 },
	{ 0x4F, 0x00 },

	{ 0xF0, 0x00 },
	{ 0xF1, 0x00 },
	{ 0xF2, 0x00 },
	{ 0xF3, 0x07 },
	{ 0xF4, 0x20 },
	{ 0xF5, 0x10 },
	{ 0xF6, 0x00 },
	{ 0xF7, 0x00 },
	{ 0xF8, 0x00 },
	{ 0xF9, 0x00 },
	{ 0xFA, 0x03 },
	{ 0xFB, 0x00 },
	{ 0xFC, 0x00 },

	{ 0x40, 0x08 },
	{ 0x00, 0x00 },
	{ 0x01, 0xf8 },
	{ 0x02, 0x00 },
	{ 0x08, 0xF0 },
	{ 0x13, 0x04 },
	{ 0x14, 0x73 },
	{ 0x15, 0x08 },
	{ 0x20, 0x12 },
	{ 0x34, 0x1b },
	{ 0x23, 0x02 },
	{ 0x23, 0x00 },

	{ 0x40, 0x00 },
};

static const SNS_REG_S s_aBt656_720p_1ch_25fps[] = {
	{ 0x02, 0xCE },
	{ 0x05, 0x00 },
	{ 0x06, 0x32 },
	{ 0x07, 0xC0 },
	{ 0x08, 0x00 },
	{ 0x09, 0x24 },
	{ 0x0A, 0x48 },
	{ 0x0B, 0xC0 },
	{ 0x0C, 0x03 },
	{ 0x0D, 0x71 },
	{ 0x0E, 0x00 },
	{ 0x0F, 0x00 },
	{ 0x10, 0x00 },
	{ 0x11, 0x40 },
	{ 0x12, 0x40 },
	{ 0x13, 0x00 },
	{ 0x14, 0x00 },
	{ 0x15, 0x13 },
	{ 0x16, 0x16 },
	{ 0x17, 0x00 },
	{ 0x18, 0x19 },
	{ 0x19, 0xD0 },
	{ 0x1A, 0x25 },
	{ 0x1B, 0x01 },
	{ 0x1C, 0x07 },
	{ 0x1D, 0xBC },
	{ 0x1E, 0x80 },
	{ 0x1F, 0x80 },
	{ 0x20, 0x40 },
	{ 0x21, 0x46 },
	{ 0x22, 0x36 },
	{ 0x23, 0x3C },
	{ 0x24, 0x04 },
	{ 0x25, 0xFE },
	{ 0x26, 0x01 },
	{ 0x27, 0x2D },
	{ 0x28, 0x00 },
	{ 0x29, 0x48 },
	{ 0x2A, 0x30 },
	{ 0x2B, 0x60 },
	{ 0x2C, 0x3A },
	{ 0x2D, 0x5A },
	{ 0x2E, 0x40 },
	{ 0x2F, 0x00 },
	{ 0x30, 0x9E },
	{ 0x31, 0x20 },
	{ 0x32, 0x10 },
	{ 0x33, 0x90 },
	{ 0x34, 0x00 },
	{ 0x35, 0x25 },
	{ 0x36, 0xDC },
	{ 0x37, 0x00 },
	{ 0x38, 0x00 },
	{ 0x39, 0x18 },
	{ 0x3A, 0x32 },
	{ 0x3B, 0x26 },
	{ 0x3C, 0x00 },
	{ 0x3D, 0x60 },
	{ 0x3E, 0x00 },
	{ 0x3F, 0x00 },
	{ 0x40, 0x00 },
	{ 0x41, 0x00 },
	{ 0x42, 0x00 },
	{ 0x43, 0x00 },
	{ 0x44, 0x00 },
	{ 0x45, 0x00 },
	{ 0x46, 0x00 },
	{ 0x47, 0x00 },
	{ 0x48, 0x00 },
	{ 0x49, 0x00 },
	{ 0x4A, 0x00 },
	{ 0x4B, 0x00 },
	{ 0x4C, 0x43 },
	{ 0x4D, 0x00 },
	{ 0x4E, 0x17 },
	{ 0x4F, 0x00 },

	{ 0xF0, 0x00 },
	{ 0xF1, 0x00 },
	{ 0xF2, 0x00 },
	{ 0xF3, 0x00 },
	{ 0xF4, 0x20 },
	{ 0xF5, 0x10 },
	{ 0xF6, 0x00 },
	{ 0xF7, 0x00 },
	{ 0xF8, 0x00 },
	{ 0xF9, 0x00 },
	{ 0xFA, 0x88 },
	{ 0xFB, 0x00 },
	{ 0xFC, 0x00 },

	{ 0x40, 0x08 },
	{ 0x01, 0xf8 },
	{ 0x02, 0x00 },
	{ 0x08, 0x0f },
	{ 0x13, 0x24 },
	{ 0x14, 0x46 },
	{ 0x15, 0x08 },
	{ 0x20, 0x12 },
	{ 0x34, 0x1b },
	{ 0x23, 0x02 },
	{ 0x23, 0x00 },

	{ 0x40, 0x00 },

};

static CAMERA_POWER_ACT_S s_stPowerUpAct[] = {
    {SNS_RESET, SNS_DOWN},
    {SNS_DELAY, 100},
    {SNS_RESET,  SNS_UP},
    {SNS_DELAY, 100},
};

static CAMERA_POWER_ACT_S s_sPowerDownAct[] = {
    {SNS_RESET, SNS_DOWN},
    {SNS_DELAY, 10},
};

static SNS_TYPE_ATTR_S s_astSnsAttr[] = {
    {
        .enSnsType                  = TP9950_DVP_1280_720_25FPS_1CH_YUV,
        .enPixelFormat              = PIX_FMT_YUV_SEMIPLANAR_420,
        .stSize                     = {1280, 720},
        .u32Mclock                  = 27000000,
        .u32ModuleClk               = 148500000,
        .u32FrameRate               = 25,
        .astRegCfgs[0].pstRegs      = s_aBt656_720p_1ch_25fps,
        .astRegCfgs[0].u32RegsCnt   = ARRAY_SIZE(s_aBt656_720p_1ch_25fps),
        .enYuvSeq                   = VISS_YUV_SEQ_C0C1C2C3,
        .enWorkMode                 = VISS_WORK_MODE_1Chn,

        .stDevCfg.stVicCfg.eDvpType = VISS_DVP_BT656,
        .stDevCfg.stVicCfg.enPclkSm = VISS_SDR,
        .stDevCfg.stVicCfg.u32IoInv = PCLK_INV,
        .stDevCfg.stVicCfg.eYcSync  = VISS_H8,
    },
    {
        .enSnsType                  = TP9950_DVP_1920_1080_25FPS_1CH_YUV,
        .enPixelFormat              = PIX_FMT_YUV_SEMIPLANAR_420,
        .stSize                     = {1920, 1080},
        .u32ModuleClk               = 148500000,
        .u32Mclock                  = 27000000,
        .u32FrameRate               = 25,
        .astRegCfgs[0].pstRegs      = s_aBt656_1080p_1ch_25fps,
        .astRegCfgs[0].u32RegsCnt   = ARRAY_SIZE(s_aBt656_1080p_1ch_25fps),
        .enYuvSeq                   = VISS_YUV_SEQ_C0C1C2C3,
        .enWorkMode                 = VISS_WORK_MODE_1Chn,

        .stDevCfg.stVicCfg.eDvpType = VISS_DVP_BT656,
        .stDevCfg.stVicCfg.enPclkSm = VISS_SDR,
        .stDevCfg.stVicCfg.u32IoInv = PCLK_INV | HS_INV,
        .stDevCfg.stVicCfg.eYcSync  = VISS_H8,
    },
    {
        .enSnsType                  = TP9950_DVP_1920_1080_30FPS_1CH_YUV,
        .enPixelFormat              = PIX_FMT_YUV_SEMIPLANAR_420,
        .stSize                     = {1920, 1080},
        .u32ModuleClk               = 148500000,
        .u32Mclock                  = 27000000,
        .u32FrameRate               = 30,
        .astRegCfgs[0].pstRegs      = s_aBt656_1080p_1ch_30fps,
        .astRegCfgs[0].u32RegsCnt   = ARRAY_SIZE(s_aBt656_1080p_1ch_30fps),
        .enYuvSeq                   = VISS_YUV_SEQ_C0C1C2C3,
        .enWorkMode                 = VISS_WORK_MODE_1Chn,

        .stDevCfg.stVicCfg.eDvpType = VISS_DVP_BT656,
        .stDevCfg.stVicCfg.enPclkSm = VISS_SDR,
        .stDevCfg.stVicCfg.u32IoInv = PCLK_INV | HS_INV,
        .stDevCfg.stVicCfg.eYcSync  = VISS_H8,
    },
};

static int CAMERA_SetI2cInfo(VISS_DEV dev)
{
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    if (pstSnsState->stI2cInfo.fd > 0) {
        return EI_SUCCESS;
    }

    pstSnsState->stI2cInfo.u8RegBits = 8;
    pstSnsState->stI2cInfo.u8ValBits = 8;
    pstSnsState->stI2cInfo.u16I2cBusNum = 2;
    pstSnsState->stI2cInfo.u16I2cDevAddr = 0x44;
    pstSnsState->pcName = SNS_NAME;

    return EI_SUCCESS;
}

static EI_S32 CAMERA_Detect(VISS_DEV VissDev)
{
    EI_U8 u8Tmp[2];
    EI_S32 s32Ret = EI_SUCCESS;
    EI_U16 u16Id;
    SNS_STATE_S *pstSnsState = EI_NULL;
    I2C_INFO_S *pstI2cInfo = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);

    pstI2cInfo = &pstSnsState->stI2cInfo;

    CAMERA_CHECK_POINTER(pstI2cInfo->pfnReadReg);
    s32Ret = pstI2cInfo->pfnReadReg(pstSnsState, 0xFE, &u8Tmp[0]);
    if (s32Ret != 0) {
        PRT_VISS_ERR("%s tmp[0]=%x\n", SNS_NAME, u8Tmp[0]);
        return s32Ret;
    }
    s32Ret = pstI2cInfo->pfnReadReg(pstSnsState, 0xFF, &u8Tmp[1]);
    if (s32Ret != 0) {
        PRT_VISS_ERR("%s tmp[0]=%x,tmp[1]=%x\n", SNS_NAME, u8Tmp[0], u8Tmp[1]);
        return s32Ret;
    }
    u16Id = (u8Tmp[0] << 8) | u8Tmp[1];
    PRT_VISS_INFO("%s id: 0x%04x\n", SNS_NAME, u16Id);

    if (u16Id != 0x2850) {
        PRT_VISS_ERR("%s ID wrong! (0x%04x != 0x2850)\n", SNS_NAME, u16Id);
        return EI_FAILURE;
    }

    return EI_SUCCESS;
}

static EI_S32 CAMERA_Stream(VISS_DEV dev, EI_BOOL bOn)
{
    SNS_STATE_S *pstSnsState = EI_NULL;
    EI_S32 s32Ret = EI_SUCCESS;
    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);
    PRT_VISS_ERR("%s CAMERA_Stream*******%d\n", SNS_NAME, bOn);
    if (bOn) {
        s32Ret = pstSnsState->stI2cInfo.pfnWriteReg(pstSnsState, 0x4E, 0x17);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("CAMERA_Stream*******%d\n", bOn);
        }
    } else {
        s32Ret = pstSnsState->stI2cInfo.pfnWriteReg(pstSnsState, 0x4E, 0x00);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("CAMERA_Stream*******%d\n", bOn);
        }
    }

    return s32Ret;
}

/* The following functions can be modified as needed */

static int CAMERA_I2cInit(VISS_DEV dev)
{
    SNS_STATE_S *pstSnsState = EI_NULL;
    int ret = EI_SUCCESS;

    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    ret = CAMERA_SetI2cInfo(dev);
    CAMERA_CHECK_SUCCESS(ret);

    ret = CAMERA_I2C_Reg_Ops(&pstSnsState->stI2cInfo);
    CAMERA_CHECK_SUCCESS(ret);

    return EI_SUCCESS;
}

static int CAMERA_I2cExit(VISS_DEV dev)
{
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    return EI_SUCCESS;
}

static EI_S32 CAMERA_PowerUp(VISS_DEV VissDev)
{
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);

    MI_VISS_SnsPower(VissDev, 0xa0000000);

    CAMERA_SensorPowerUp(VissDev, pstSnsState);

    return 0;
}

static EI_S32 CAMERA_PowerDown(VISS_DEV VissDev)
{
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);

    MI_VISS_SnsPower(VissDev, 0x00000000);

    CAMERA_SensorPowerDown(VissDev, pstSnsState);

    return 0;
}

static EI_S32 CAMERA_I2cWrite(VISS_DEV VissDev, EI_U32 u32Ch, EI_U32 u32Reg, EI_U32 u32Val)
{
    EI_S32 s32Ret = EI_SUCCESS;
    SNS_STATE_S *pstSnsState = EI_NULL;
    I2C_INFO_S *pstI2cInfo = EI_NULL;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);
    pstI2cInfo = &pstSnsState->stI2cInfo;
    CAMERA_CHECK_POINTER(pstI2cInfo->pfnWriteReg);
    pStCam = (CAMERA_S *)pstSnsState->pCam;

    pthread_mutex_lock(&pStCam->mutex);
    s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, 0x40, u32Ch);
    s32Ret = pstI2cInfo->pfnWriteReg(pstSnsState, u32Reg, u32Val);
    pthread_mutex_unlock(&pStCam->mutex);

    return s32Ret;
}

static EI_S32 CAMERA_I2cRead(VISS_DEV VissDev, EI_U32 u32Ch, EI_U32 u32Reg, EI_U32 *pu32Val)
{
    EI_S32 s32Ret = EI_SUCCESS;
    SNS_STATE_S *pstSnsState = EI_NULL;
    I2C_INFO_S *pstI2cInfo = EI_NULL;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);
    pstI2cInfo = &pstSnsState->stI2cInfo;
    CAMERA_CHECK_POINTER(pstI2cInfo->pfnReadReg);
    pStCam = (CAMERA_S *)pstSnsState->pCam;

    pthread_mutex_lock(&pStCam->mutex);
    s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, 0x40, u32Ch);
    s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, u32Reg, pu32Val);
    pthread_mutex_unlock(&pStCam->mutex);

    return s32Ret;
}

static EI_S32 CAMERA_Init(VISS_DEV dev)
{
    EI_U32 j;
    const SNS_TYPE_ATTR_S *pstSnsType;
    SNS_STATE_S *pstSnsState = EI_NULL;
    EI_S32 s32Ret;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    pStCam = (CAMERA_S *)pstSnsState->pCam;
    pstSnsType = pstSnsState->pstSnsAttr;

    pthread_mutex_lock(&pStCam->mutex);

    for (j = 0; j < MAX_COF; j++) {
        s32Ret = pstSnsState->stI2cInfo.pfnwriteRegList(pstSnsState,
                pstSnsType->astRegCfgs[j].pstRegs, pstSnsType->astRegCfgs[j].u32RegsCnt);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s writeRegList err", SNS_NAME);
            pthread_mutex_unlock(&pStCam->mutex);
            return s32Ret;
        }
    }

    pthread_mutex_unlock(&pStCam->mutex);

    PRT_VISS_INFO("%s writeRegList success", SNS_NAME);

    return s32Ret;
}

static void CAMERA_Exit(VISS_DEV dev)
{
    CAMERA_I2cExit(dev);

    return;
}

static EI_S32 CAMERA_SetImageMode(VISS_DEV dev, SNS_TYPE_E enSnsType)
{
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(dev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    pstSnsState->enSnsType = enSnsType;
    pstSnsState->bSyncInit = EI_FALSE;

    return EI_SUCCESS;
}

static EI_S32 CAMERA_EnumSnstype(VISS_DEV VissDev, EI_U32 u32Index, SNS_TYPE_E *peSnstype)
{
    const SNS_TYPE_ATTR_S *pstSnsType;

    if (u32Index >= ARRAY_SIZE(s_astSnsAttr))
        return EN_ERR_NO_FOUND;

    pstSnsType = &s_astSnsAttr[u32Index];

    *peSnstype = pstSnsType->enSnsType;

    return EI_SUCCESS;
}

static EI_S32 CAMERA_GetStatus(VISS_DEV VissDev, SNS_STS_S *pstSts)
{
    const SNS_TYPE_ATTR_S *pstSnsType;
    SNS_STATE_S *pstSnsState = EI_NULL;
    EI_S32 s32Ret = EI_SUCCESS;
    EI_U32 i;
    I2C_INFO_S *pstI2cInfo = EI_NULL;
    EI_U8 tmp;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    pStCam = (CAMERA_S *)pstSnsState->pCam;
    pstI2cInfo = &pstSnsState->stI2cInfo;
    pstSnsType = pstSnsState->pstSnsAttr;

    pthread_mutex_lock(&pStCam->mutex);

    for (i = 0; i < pstSnsType->enWorkMode; i++) {
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, 0x40, (0x00 + i));
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            pthread_mutex_unlock(&pStCam->mutex);
            return s32Ret;
        }

        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, 0x01, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
#if 0
        tmp = (tmp & 0x80) >> 7;
        if (!tmp) {
            s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, 0x2f, &tmp);
            if (s32Ret != EI_SUCCESS) {
                PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
                return s32Ret;
            }

            if (0x80 == tmp) {
                s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, 0x04, &tmp);
                if (s32Ret != EI_SUCCESS) {
                    PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
                    return s32Ret;
                }

                if (tmp < 0x30)
                    tmp = 0;
                else
                    tmp = 1;
            }
        }
#endif
        if ((tmp & (0x1 << 5)) && (tmp & (0x1 << 6)))
            tmp = 0;
        else
            tmp = 1;
        pstSts->bChnSts[i] = tmp;

    }

    pthread_mutex_unlock(&pStCam->mutex);

    return s32Ret;
}

static EI_S32 CAMERA_GetImage(VISS_DEV VissDev, SNS_IMAGE_S *pstSts)
{
    const SNS_TYPE_ATTR_S *pstSnsType;
    SNS_STATE_S *pstSnsState = EI_NULL;
    EI_S32 s32Ret = EI_SUCCESS;
    EI_U32 i;
    I2C_INFO_S *pstI2cInfo = EI_NULL;
    EI_U8 tmp;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    pStCam = (CAMERA_S *)pstSnsState->pCam;
    pstI2cInfo = &pstSnsState->stI2cInfo;
    pstSnsType = pstSnsState->pstSnsAttr;

    pthread_mutex_lock(&pStCam->mutex);

    for (i = 0; i < pstSnsType->enWorkMode; i++) {
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, 0x40, (0x00 + i));
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            pthread_mutex_unlock(&pStCam->mutex);
            return s32Ret;
        }

        /* Get Brightness */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, BRIGHTNESS, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
        pstSts->brightness[i] = tmp;
        /* Get Contrast */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, CONTRAST, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
        pstSts->contrast[i] = tmp;
        /* Get Saturation */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, SATURATION, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
        pstSts->saturation[i] = tmp;
        /* Get Hue */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, HUE, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
        pstSts->hue[i] = tmp;
        /* Get Sharpness */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, SHARPNESS, &tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnReadReg err", SNS_NAME);
        }
        pstSts->sharpness[i] = (tmp & 0x1F);
    }

    pthread_mutex_unlock(&pStCam->mutex);

    return s32Ret;
}

static EI_S32 CAMERA_SetImage(VISS_DEV VissDev, SNS_IMAGE_S *pstSts)
{
    const SNS_TYPE_ATTR_S *pstSnsType;
    SNS_STATE_S *pstSnsState = EI_NULL;
    EI_S32 s32Ret = EI_SUCCESS;
    EI_U32 i;
    I2C_INFO_S *pstI2cInfo = EI_NULL;
    EI_U8 tmp;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(VissDev, pstSnsState);
    CAMERA_CHECK_POINTER(pstSnsState);

    pStCam = (CAMERA_S *)pstSnsState->pCam;
    pstI2cInfo = &pstSnsState->stI2cInfo;
    pstSnsType = pstSnsState->pstSnsAttr;

    pthread_mutex_lock(&pStCam->mutex);

    for (i = 0; i < pstSnsType->enWorkMode; i++) {
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, 0x40, (0x00 + i));
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }

        /* Set Brightness */
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, BRIGHTNESS, pstSts->brightness[i]);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }
        /* Set Contrast */
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, CONTRAST, pstSts->contrast[i]);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }
        /* Set Saturation */
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, SATURATION, pstSts->saturation[i]);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }
        /* Set Hue */
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, HUE, pstSts->hue[i]);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }
        /* Set Sharpness */
        tmp = 0;
        s32Ret += pstI2cInfo->pfnReadReg(pstSnsState, SHARPNESS, &tmp);
        tmp &= 0xe0;
        tmp |= (pstSts->sharpness[i] & 0x1F);
        s32Ret += pstI2cInfo->pfnWriteReg(pstSnsState, SHARPNESS, tmp);
        if (s32Ret != EI_SUCCESS) {
            PRT_VISS_ERR("%s pfnWriteReg err", SNS_NAME);
            return s32Ret;
        }

    }

    pthread_mutex_unlock(&pStCam->mutex);

    return s32Ret;
}


static EI_S32 CAMERA_InitSensorExpFunction(SENSOR_EXP_FUNC_S *pstSensorExpFunc)
{
    CAMERA_CHECK_POINTER(pstSensorExpFunc);

    memset(pstSensorExpFunc, 0, sizeof(SENSOR_EXP_FUNC_S));

    pstSensorExpFunc->pfnSensorPowerUp  = CAMERA_PowerUp;
    pstSensorExpFunc->pfnSensorPowerDown = CAMERA_PowerDown;
    pstSensorExpFunc->pfnSensorDetect   = CAMERA_Detect;
    pstSensorExpFunc->pfnSensorInit     = CAMERA_Init;
    pstSensorExpFunc->pfnSensorStream   = CAMERA_Stream;
    pstSensorExpFunc->pfnSensorExit     = CAMERA_Exit;
    pstSensorExpFunc->pfnSensorSetImageMode   = CAMERA_SetImageMode;
    pstSensorExpFunc->pfnSensorEnumSnstype   = CAMERA_EnumSnstype;
    pstSensorExpFunc->pfnSensorGetStatus   = CAMERA_GetStatus;
    pstSensorExpFunc->pfnSensorSetImage       = CAMERA_SetImage;
    pstSensorExpFunc->pfnSensorGetImage       = CAMERA_GetImage;
    pstSensorExpFunc->pfnSensorI2cWrite       = CAMERA_I2cWrite;
    pstSensorExpFunc->pfnSensorI2cRead        = CAMERA_I2cRead;

    return EI_SUCCESS;
}


/****************************************************************************
 * callback structure                                                       *
 ****************************************************************************/

static EI_S32 CAMERA_CtxInit(VISS_DEV dev)
{
    SNS_STATE_S *pastSnsStateCtx = EI_NULL;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(dev, pastSnsStateCtx);
    CAMERA_CHECK_POINTER(pastSnsStateCtx);

    memset(pastSnsStateCtx, 0, sizeof(SNS_STATE_S));

    pastSnsStateCtx->pCam = malloc(sizeof(CAMERA_S));
    pStCam = (CAMERA_S *)pastSnsStateCtx->pCam;
    pthread_mutex_init(&pStCam->mutex, NULL);

    pastSnsStateCtx->pstPowerUpAct      = s_stPowerUpAct;
    pastSnsStateCtx->pstPowerDownAct    = s_sPowerDownAct;
    pastSnsStateCtx->u8PwrUpActs        = ARRAY_SIZE(s_stPowerUpAct);
    pastSnsStateCtx->u8PwrDnActs        = ARRAY_SIZE(s_sPowerDownAct);

    return EI_SUCCESS;
}

static EI_S32 CAMERA_CtxExit(VISS_DEV dev)
{
    SNS_STATE_S *pastSnsStateCtx = EI_NULL;
    CAMERA_S *pStCam = EI_NULL;

    SNS_GET_CTX(dev, pastSnsStateCtx);
    CAMERA_CHECK_POINTER(pastSnsStateCtx);

    pStCam = (CAMERA_S *)pastSnsStateCtx->pCam;
    pthread_mutex_destroy(&pStCam->mutex);

    if (pStCam) {
        free(pStCam);
        pStCam = EI_NULL;
    }

    return EI_SUCCESS;
}

static EI_S32 CAMERA_RegisterVissCallback(VISS_DEV dev, SNS_TYPE_E enType)
{
    EI_S32 s32Ret;
    SENSOR_S stSns = {0};
    EI_U32 i;
    SNS_STATE_S *pstSnsState = EI_NULL;

    s32Ret = CAMERA_CtxInit(dev);
    if (EI_SUCCESS != s32Ret) {
        return EI_FAILURE;
    }

    SNS_GET_CTX(dev, pstSnsState);

    for (i = 0; i < ARRAY_SIZE(s_astSnsAttr); i++) {
        if (enType == s_astSnsAttr[i].enSnsType) {
            pstSnsState->pstSnsAttr = &s_astSnsAttr[i];
            break;
        }
    }

    pstSnsState->enSnsType = enType;
    pstSnsState->Dev = dev;

    if (i >= ARRAY_SIZE(s_astSnsAttr)) {
        PRT_VISS_ERR("%s does not support this chn configuration", SNS_NAME);
        return EI_FAILURE;
    }

    s32Ret = CAMERA_I2cInit(dev);
    if (EI_SUCCESS != s32Ret) {
        return EI_FAILURE;
    }

    stSns.stSnsAttrInfo.eSensorId = SNS_ID;
    stSns.stSnsAttrInfo.enDevType = DEV_TYPE;
    stSns.stSnsAttrInfo.pstSns = pstSnsState;

    s32Ret  = CAMERA_InitSensorExpFunction(&stSns.stSnsAttrInfo.stSnsExp);
    s32Ret |= VISS_SensorRegCallBack(dev, &stSns);
    if (EI_SUCCESS != s32Ret) {
        PRT_VISS_ERR("%s sensor register callback function failed!\n", SNS_NAME);
        return s32Ret;
    }

    s32Ret = CAMERA_OpenI2c(pstSnsState);
    CAMERA_CHECK_SUCCESS(s32Ret);

    return EI_SUCCESS;
}

static EI_S32 CAMERA_UnregisterVissCallback(VISS_DEV dev)
{
    EI_S32 s32Ret;
    SNS_STATE_S *pstSnsState = EI_NULL;

    SNS_GET_CTX(dev, pstSnsState);

    s32Ret = CAMERA_ExitI2c(pstSnsState);
    CAMERA_CHECK_SUCCESS(s32Ret);

    s32Ret = VISS_SensorUnRegCallBack(dev, SNS_ID);
    if (EI_SUCCESS != s32Ret) {
        PRT_VISS_ERR("%s sensor unregister callback function failed!\n", SNS_NAME);
        return s32Ret;
    }

    CAMERA_CtxExit(dev);

    return EI_SUCCESS;
}

static EI_S32 CAMERA_GetVissDevAttrBySns(SNS_TYPE_E enType,
    VISS_DEV_ATTR_S *pstVissDevAttr)
{
    EI_U32 i;
    const SNS_TYPE_ATTR_S *pstSnsType;

    for (i = 0; i < ARRAY_SIZE(s_astSnsAttr); i++) {
        if (enType == s_astSnsAttr[i].enSnsType) {
            pstSnsType = &s_astSnsAttr[i];
            pstVissDevAttr->stSize          = pstSnsType->stSize;
            pstVissDevAttr->enWorkMode      = pstSnsType->enWorkMode;
            pstVissDevAttr->mClock          = pstSnsType->u32Mclock;
            pstVissDevAttr->enPixelFormat   = pstSnsType->enPixelFormat;
            pstVissDevAttr->enWdrMode       = pstSnsType->enWdrMode;
            pstVissDevAttr->enIspWdrMode    = pstSnsType->enIspWdrMode;
            pstVissDevAttr->enRgbSeq        = pstSnsType->enRgbSeq;
            pstVissDevAttr->u32ModuleClk    = pstSnsType->u32ModuleClk;
            pstVissDevAttr->u32Fps          = pstSnsType->u32FrameRate;
            pstVissDevAttr->u32IspClk       = pstSnsType->u32IspClk;
            pstVissDevAttr->u32IppuClk      = pstSnsType->u32IppuClk;
            pstVissDevAttr->pcSnsName       = SNS_NAME;
            pstVissDevAttr->stDevCfg        = pstSnsType->stDevCfg;
            return EI_SUCCESS;
        }
    }

    PRT_VISS_ERR("%s does not support this dev configuration", SNS_NAME);

    return EI_FAILURE;
}

static EI_S32 CAMERA_GetVissChnAttrBySns(SNS_TYPE_E enType,
    VISS_CHN_ATTR_S *pstVissChnAttr)
{
    EI_U32 i;
    const SNS_TYPE_ATTR_S *pstSnsType;

    for (i = 0; i < ARRAY_SIZE(s_astSnsAttr); i++) {
        if (enType == s_astSnsAttr[i].enSnsType) {
            pstSnsType = &s_astSnsAttr[i];
            pstVissChnAttr->stSize           = pstSnsType->stSize;
            pstVissChnAttr->enPixelFormat    = pstSnsType->enPixelFormat;
            pstVissChnAttr->stFrameRate.s32SrcFrameRate      = pstSnsType->u32FrameRate;
            return EI_SUCCESS;
        }
    }

    PRT_VISS_ERR("%s does not support this chn configuration", SNS_NAME);

    return EI_FAILURE;

}

CAMERA_OBJ_S stCameraTp9950dvpObj = {
    .pfnRegisterVissCallback    = CAMERA_RegisterVissCallback,
    .pfnUnRegisterVissCallback  = CAMERA_UnregisterVissCallback,
    .pfnRegisterIspCallback     = EI_NULL,
    .pfnUnRegisterIspCallback   = EI_NULL,
    .pfnGetVissDevAttrBySns     = CAMERA_GetVissDevAttrBySns,
    .pfnGetVissChnAttrBySns     = CAMERA_GetVissChnAttrBySns,
};

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

